package net.cloudsun.tree.binary;

import net.cloudsun.tree.util.CharArrayUtils;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 节点字符串
 *
 * @author Ryan
 * @since 1.2
 */
public class NodeString<T> {

    /**
     * x 坐标
     */
    int x;
    /**
     * y坐标
     */
    int y;
    /**
     * 长度
     */
    int length;
    /**
     * 索引同一行的第几个
     */
    int xIndex;

    /**
     * 节点
     */
    protected BinaryNode<T> node;
    protected Map<BinaryNode<T>, NodeString<T>> map;

    /**
     * @param map
     * @param prev
     * @return
     */
    public NodeString(BinaryNode<T> node, Map<BinaryNode<T>, NodeString<T>> map, NodeString prev) {
        this.length = node.toString().length();
        this.node = node;
        this.map = map;
        map.put(node, this);

        // 因为不一定是排序二叉树，所以不能用大小进行比较
        if (this.getParent() == null) {
            return;
        }

        final NodeString parentString = map.get(this.getParent());
        if (parentString == null) {
            return;
        }
        // 找准自己的y轴定位
        this.y = parentString.y + 1;
        if (this.y == prev.y) {
            this.xIndex = prev.xIndex + 1;
        } else {
            this.xIndex = 0;
        }

        int startIndex = getStartIndex();
        if (node.isLeftChild()) {
            this.x = parentString.x - 1;
            if (this.x < startIndex) {
                this.x = startIndex;
                this.moveParent();
            }
        } else {

            this.x = Math.max(parentString.x + 1, startIndex);
        }

    }

    @Override
    public String toString() {
        return "x" + x + "y" + y + "l" + length;
    }


    /**
     * 移动哥哥们
     * 根据自己的坐标去移动哥哥
     */
    private void moveOlderBrother() {
        final int startIndex = this.getMaxX();
        moveSibling(startIndex);
    }

    /**
     * 移动兄弟
     * @param startIndex 开始索引
     */
    private void moveSibling(int startIndex) {
        final Stream<Map.Entry<BinaryNode<T>, NodeString<T>>> entryStream = map.entrySet().stream().filter(
                entry -> entry.getValue().y == this.y && entry.getValue().xIndex == this.xIndex + 1);
        final List<Map.Entry<BinaryNode<T>, NodeString<T>>> list = entryStream.collect(Collectors.toList());
        if (list.isEmpty()) {
            return;
        }
        final NodeString sibling = list.get(0).getValue();

        if (sibling.x <= startIndex) {
            // 关键在于如何判断该在它右边的值
            final int diff = startIndex - sibling.x;
            //移动diff + 1
            sibling.x += (diff + 1);
            // left length 到底是什么？
            // 没有移动自己的兄弟
            if (sibling.node.isLeftChild()) {
                sibling.moveParent();
            }
            sibling.moveOlderBrother();
        }
    }


    /**
     * 从自身向上级循环，整体向右移动
     */
    protected void moveParent() {
        BinaryNode<T> prev = this.node;
        for (BinaryNode<T> p = this.getParent(); p != null; p = p.getParent()) {
            // 递归地把parent移动
            NodeString n = map.get(p);
            if (n == null) {
                break;
            }
            if (prev == n.node.getLeft()) {
                final NodeString nodeString = map.get(prev);
                final int newX = nodeString.x + 1;
                if (newX > n.x) {
                    n.x = newX;
                }
            } else if (prev == n.node.getRight()) {
                // 右子
                n.moveSibling(map.get(prev).x);
            }
            n.moveOlderBrother();
            prev = p;
        }
    }


    /**
     * 添加父子连接字符串
     *
     * @param chars
     */
    public void addParentLink(char[][] chars) {
        if (this.y == 0) {
            return;
        }
        final char[] prevLine = chars[(this.y << 1) - 1];
        final boolean leftChild = this.node.isLeftChild();
        final int parentX = map.get(this.getParent()).x;
        final int thisX = this.x;
        CharArrayUtils.drawLine(prevLine, leftChild, parentX, thisX);
    }

    // 伪字段

    int getMaxX() {
        return x + length;
    }
    private int getStartIndex() {
        final Optional<Integer> max = map.entrySet().stream().filter(e -> e.getValue().y == this.y && e.getValue().xIndex != xIndex)
                .map(e -> e.getValue().getMaxX()).max(Integer::compareTo);
        return max.orElse(-1) + 1;
    }

    private BinaryNode<T> getParent() {
        return node == null ? null : node.getParent();
    }
    // getter setter 放最后

    public int getY() {
        return y;
    }

    public int getX() {
        return x;
    }

    public int getLength() {
        return length;
    }
}
